package me.spring.cloud.components.starter.business.log.aspect;

import com.alibaba.fastjson.JSONObject;
import me.spring.cloud.common.components.oauth2.userdetails.exception.AuthUserException;
import me.spring.cloud.common.components.oauth2.userdetails.exception.code.AuthoritiesCode;
import me.spring.cloud.common.components.rabbitmq.util.RabbitMqUtil;
import me.spring.cloud.common.components.util.system.RemoteAddressUtil;
import me.spring.cloud.components.starter.business.log.annotation.BusinessLog;
import me.spring.cloud.components.starter.business.log.properties.BusinessProperties;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author YuanWei
 * @description: 业务日志切面
 * @date 2019/3/27 17:44
 */
@Slf4j
@Aspect
public class BusinessLogAspect {

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private BusinessProperties businessProperties;

    @Pointcut("@annotation(me.spring.cloud.components.starter.business.log.annotation.BusinessLog)")
    public void businessLog() {}


    @Around("businessLog()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;

        //MQ发送日志
        if (businessProperties.isEnable())
            try {
                mqSend(point, time);
            }catch (Exception e){
                log.error("api operation error e:{}",e);
            }

        return result;
    }

    private void mqSend(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        JSONObject jo = new JSONObject();
        BusinessLog businessLog = method.getAnnotation(BusinessLog.class);
        if(businessLog != null){
            //注解上的描述
            jo.put("desc", businessLog.desc());
        }
        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        jo.put("method", className + "." + methodName + "()");

        //请求的参数
        Object[] args = joinPoint.getArgs();
        args = Arrays.asList(args).stream().filter(a->!(a instanceof HttpServletRequest || a instanceof HttpServletResponse)).collect(Collectors.toList()).toArray();
        jo.put("args",args);

        //获取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (request != null){
            jo.put("clientIp",RemoteAddressUtil.getIpAddress(request));
        }else {
            jo.put("clientIp","");
        }

        //用户昵称
        Object userInfo = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (null == userInfo || "unknown".equals(userInfo)) {
            throw new AuthUserException(AuthoritiesCode.CLIENT_VALID_TOKEN_ERROR);
        }
        if (userInfo instanceof  LinkedHashMap){
            Map details = (LinkedHashMap)userInfo;
            jo.put("username",details.get("username"));
        }else {
            jo.put("username","");
        }
        //执行时长
        jo.put("executeTime",time);
        //创建时间
        jo.put("createTime", new Date());

        //发送mq
        RabbitMqUtil.sendMessage(amqpTemplate, businessLog.queue(), jo.toJSONString());
    }
}
